package org.dreamwork.cai.tools;

import org.dreamwork.cai.SRFException;
import org.dreamwork.cai.annotation.ADescription;
import org.dreamwork.cai.annotation.ARemoteExport;
import org.dreamwork.cai.annotation.ARemoteService;
import org.dreamwork.cai.srf.MethodInfo;
import org.dreamwork.cai.srf.ParameterInfo;
import org.dreamwork.cai.srf.ServiceRouteFrame;
import org.dreamwork.cai.srf.ServiceRouteItem;
import org.dreamwork.secure.AlgorithmMapping;
import org.dreamwork.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

import static org.dreamwork.cai.tools.Const.JMX_GROUP;

/**
 * 杂项工具类
 */
public class Misc {
    private static final Logger logger = LoggerFactory.getLogger (Misc.class);
    private static final MBeanServer server  = ManagementFactory.getPlatformMBeanServer ();

    /**
     * 计算指定对象 {@code o} 的稳定的 {@code 哈希值}.
     *
     * <p>这个方法计算结果的稳定性依赖于对象的 {@code toString()}方法的稳定性。</p>
     * @param o 指定对象
     * @return 对象的 SHA256 哈希值
     */
    public static String sha256 (Object o) {
        if (o == null) {
            return null;
        }

        byte[] buff = o.toString ().getBytes (StandardCharsets.UTF_8);
        try {
            MessageDigest md = MessageDigest.getInstance (AlgorithmMapping.MessageDigest.SHA256.jceName);
            buff = md.digest (buff);
            return StringUtil.byte2hex (buff, false);
        } catch (NoSuchAlgorithmException ex) {
            throw new RuntimeException (ex);
        }
    }

    /**
     * 将字符串形式的类名翻译成 jvm 的 Class
     * @param type 类名
     * @return jvm 的 Class 对象
     * @throws ClassNotFoundException 如果无法加载类
     */
    public static Class<?> translate (String type) throws ClassNotFoundException {
        switch (type) {
            case "boolean":
                return boolean.class;
            case "byte":
                return byte.class;
            case "short":
                return short.class;
            case "char":
                return char.class;
            case "int":
                return int.class;
            case "long":
                return long.class;
            case "float":
                return float.class;
            case "double":
                return double.class;
            default:
                return Class.forName (type);
        }
    }

    /**
     * 补充方法额外信息
     * @param src         原 sri
     * @param sri         目标 sri
     * @param declareType 声明的返回类型
     * @throws NoSuchMethodException 如果方法未找到
     * @throws ClassNotFoundException 如果类无法加载
     * @throws SRFException 如果有 SRF 异常
     */
    public static void patchMethodInfo (ServiceRouteItem src, ServiceRouteItem sri, Class<?> declareType) throws NoSuchMethodException, ClassNotFoundException, SRFException {
        Set<MethodInfo> copy = new HashSet<> (src.methods);
        src.methods.clear ();
        for (MethodInfo info : copy) {
            String name = info.name;
            List<ParameterInfo> pis = info.parameters;
            Class<?>[] pts = new Class[pis.size ()];
            for (int i = 0; i < pts.length; i ++) {
                pts [i] = Misc.translate (pis.get (i).type);
            }

            Method method = declareType.getMethod (name, pts);
            MethodInfo mi = new MethodInfo (method);
            mi.uuid = info.uuid;
            sri.add (mi);
        }
    }

    /**
     * 将指定的对象 {@code mbean} 注册未指定的名称 {@code name}
     * @param name  mbean 注册名称
     * @param mbean mbean 对象
     * @return mbean名称
     */
    public static ObjectName registerJMXService (String name, Object mbean) {
        try {
            ObjectName oName = new ObjectName (JMX_GROUP, "name", name);
            server.registerMBean (mbean, oName);
            return oName;
        } catch (Exception ex) {
            throw new RuntimeException (ex);
        }
    }

    /**
     * 反注册指定名称 {@code oName} 的mbean
     * @param oName mbean 名称
     */
    public static void unregisterJMXService (ObjectName oName) {
        try {
            server.unregisterMBean (oName);
        } catch (Exception ex) {
            throw new RuntimeException (ex);
        }
    }

    /**
     * 根据给定类{@code type}, 对象 {@code bean} 构建本地 SRF
     * @param type     对象 {@code bean} 的指定类型
     * @param srf      服务路由帧
     * @param bean     给定对象
     * @param beanName srf 的备用名称
     * @throws SRFException 如果有 srf 异常
     */
    public static void buildLocalSRF (Class<?> type, ServiceRouteFrame srf, Object bean, String beanName) throws SRFException {
        if (type.isAnnotationPresent (ARemoteService.class)) {
            if (logger.isTraceEnabled ()) {
                logger.trace ("found a service annotated as {}", bean);
            }
            ARemoteService rs = type.getAnnotation (ARemoteService.class);
            String endpoint = rs.endpoint ();
            if (StringUtil.isEmpty (endpoint)) {
                endpoint = beanName;
            }
            ServiceRouteItem sri = new ServiceRouteItem (endpoint, srf);
            // 填充 SRI 的描述
            if (!StringUtil.isEmpty (rs.description ())) {
                sri.setDescription (rs.description ());
            } else if (type.isAnnotationPresent (ADescription.class)) {
                ADescription ad = type.getAnnotation (ADescription.class);
                if (!StringUtil.isEmpty (ad.value ())) {
                    sri.setDescription (ad.value ());
                }
            }

            // 填充 SRI 的类型
            determineType (sri, type, rs);

            // 开始扫描这个类的所有标注为 ARemoteExport 的 公共方法
            Method[] all = type.getMethods ();
            for (Method method : all) {
                if ((method.getModifiers () & Modifier.PUBLIC) != 0 && method.isAnnotationPresent (ARemoteExport.class)) {
                    // 只导出公共方法
                    sri.add (new MethodInfo (method));
                }
            }

            if (!sri.methods.isEmpty ()) {
                srf.add (sri);
            }
        }
    }

    /**
     * 将java类型名称转换成 java 属性风格的字符串
     * @param type java 类型
     * @return java 属性风格的字符串
     */
    public static String getSimpleName (Class<?> type) {
        String name = type.getSimpleName ();
        if (name.length () == 1) {
            return name.toLowerCase ();
        }
        return Character.toLowerCase (name.charAt (0)) + name.substring (1);
    }

    /**
     * 填充 SRI 的类型
     * @param sri     服务路由项
     * @param rawType 原始类型
     * @param rs      远程服务标注
     */
    private static void determineType (ServiceRouteItem sri, Class<?> rawType, ARemoteService rs) {
        if (rs.types ().length > 0) {
            // 若注解中指定了导出的接口类型，直接使用
            List<Class<?>> temp = filter (rs.types ());
            if (!temp.isEmpty ()) {
                sri.setTypes (temp);
            }
        } else {
            // 注解中未指定，则从该类实际实现的接口来查找
            Class<?>[] types = rawType.getInterfaces ();
            if (types.length > 0) {
                List<Class<?>> temp = filter (types);
                if (!temp.isEmpty ()) {
                    sri.setTypes (temp);
                }
            } else {

                sri.setTypes (Arrays.asList (rawType));
            }
        }
    }

    /**
     * 过滤某些特征的类型
     * @param types 待过滤的类型列表
     * @return 过滤后的类型列表
     */
    private static List<Class<?>> filter (Class<?>[] types) {
        List<Class<?>> temp = new ArrayList<> ();
        for (Class<?> it : types) {
            String name = it.getCanonicalName ();
            if (!name.startsWith ("java.") && !name.startsWith ("javax.")) {
                temp.add (it);
            }
        }
        return temp;
    }

    /**
     * 查找当前有效的所有网络接口
     * @return 当前所有有效的网络接口
     * @throws SocketException 如果发生网络异常
     */
    public static List<NetworkInterface> findAllValidNetworkInterfaces () throws SocketException {
        List<NetworkInterface> list = new ArrayList<> ();
        Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces ();
        while (nis.hasMoreElements ()){
            NetworkInterface ni = nis.nextElement ();
            if (ni.isUp () && !ni.isLoopback () && ni.supportsMulticast ()) {
                list.add (ni);
            }
        }
        return list;
    }

    private static final Set<String> LOCAL_ADDRESSES = new HashSet<> ();
    static {
        try {
            appendLocalAddress (NetworkInterface.getNetworkInterfaces ());
        } catch (IOException ex) {
            ex.getMessage ();
        }
    }

    private static void appendLocalAddress (Enumeration<NetworkInterface> e) throws SocketException {
        while (e.hasMoreElements ()) {
            NetworkInterface ni = e.nextElement ();
            if (ni.isUp ()) {
                Enumeration<InetAddress> en = ni.getInetAddresses ();
                while (en.hasMoreElements ()) {
                    InetAddress addr = en.nextElement ();

                    if (addr instanceof Inet4Address) { // 只关心 IPv4
                        LOCAL_ADDRESSES.add (addr.getHostAddress ());
                    }

                    appendLocalAddress (ni.getSubInterfaces ());
                }
            }
        }
    }

    /**
     * 判断给定的主机地址是否是本地某个网络接口的地址
     * @param host 给定的主机地址
     * @return 如果给定的主机地址是某个本地网络接口的地址返回 true，否在 false
     */
    public static boolean isLocalAddress (String host) {
        return LOCAL_ADDRESSES.contains (host);
    }
}
